package com.whfc.mach.manager.util;

import com.whfc.common.util.DateUtil;
import com.whfc.common.util.JSONUtil;
import com.whfc.mach.dto.AppMachDaySegDTO;
import com.whfc.mach.dto.WorkStateDTO;
import com.whfc.mach.enums.NetState;
import com.whfc.mach.enums.WorkState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @Description: 设备分段工具类
 * @author: xugcheng
 * @version: 1.0
 * @date: 2019/12/10 18:03
 */
public class MachSegmentUtil {

    private static Logger logger = LoggerFactory.getLogger(MachSegmentUtil.class);

    /**
     * 离线时间间隔s
     */
    private static final Integer OFFLINE_INTERVAL_TIMES = 150 * 60;

    /**
     * 原始工时分段
     *
     * @param machDataLogList
     * @return
     */
    public static List<AppMachDaySegDTO> separateMachDaySegment(List<? extends WorkStateDTO> machDataLogList) {
        // 数据分段
        boolean isStop = false;
        boolean isIdle = false;
        boolean isRun = false;
        List<AppMachDaySegDTO> segList = new ArrayList<>();
        Date segBegin = null;
        Date segEnd = null;
        for (int i = 0; i < machDataLogList.size(); i++) {
            WorkStateDTO data = machDataLogList.get(i);
            Date end = data.getTime();
            int diffMinutes = 1;
            Date begin = DateUtil.addMinutes(end, -1 * diffMinutes);
            // 初始化分段时间
            if (i == 0) {
                segBegin = begin;
                segEnd = end;
            }
            // 静止数据点
            if (MachUtil.isStopState(data.getWorkState())) {
                // 静止->静止
                if (isStop) {
                    segEnd = end;
                }
                // 怠机->静止(生成怠机分段)
                else if (isIdle) {
                    if (segEnd.before(begin)) {
                        segEnd = begin;
                    }
                    AppMachDaySegDTO seg = buildMachDaySegment(segBegin, segEnd, WorkState.IDLE);
                    segList.add(seg);
                    segBegin = begin.before(segEnd) ? segEnd : begin;
                    segEnd = end;
                    isStop = true;
                    isIdle = false;
                }
                // 运动->静止(生成运动分段)
                else if (isRun) {
                    if (segEnd.before(begin)) {
                        segEnd = begin;
                    }
                    AppMachDaySegDTO seg = buildMachDaySegment(segBegin, segEnd, WorkState.RUN);
                    segList.add(seg);
                    segBegin = begin.before(segEnd) ? segEnd : begin;
                    segEnd = end;
                    isStop = true;
                    isRun = false;
                }
                // 初始化
                else {
                    isStop = true;
                }
            }
            // 怠机数据点
            else if (MachUtil.isIdleState(data.getWorkState())) {
                // 静止->怠机(生成静止分段)
                if (isStop) {
                    if (segEnd.before(begin)) {
                        segEnd = begin;
                    }
                    AppMachDaySegDTO seg = buildMachDaySegment(segBegin, segEnd, WorkState.STOP);
                    segList.add(seg);
                    segBegin = begin.before(segEnd) ? segEnd : begin;
                    segEnd = end;
                    isIdle = true;
                    isStop = false;
                }
                // 怠机->怠机
                else if (isIdle) {
                    segEnd = end;
                }
                // 工作->怠机(生成工作分段)
                else if (isRun) {
                    if (segEnd.before(begin)) {
                        segEnd = begin;
                    }
                    AppMachDaySegDTO seg = buildMachDaySegment(segBegin, segEnd, WorkState.RUN);
                    segList.add(seg);
                    segBegin = begin.before(segEnd) ? segEnd : begin;
                    segEnd = end;
                    isIdle = true;
                    isRun = false;
                }
                // 初始化
                else {
                    isIdle = true;
                }
            }
            // 工作数据点
            else if (MachUtil.isRunState(data.getWorkState())) {
                // 静止->工作(生成静止分段)
                if (isStop) {
                    if (segEnd.before(begin)) {
                        segEnd = begin;
                    }
                    AppMachDaySegDTO seg = buildMachDaySegment(segBegin, segEnd, WorkState.STOP);
                    segList.add(seg);
                    segBegin = begin.before(segEnd) ? segEnd : begin;
                    segEnd = end;
                    isRun = true;
                    isStop = false;
                }
                // 怠机->工作
                else if (isIdle) {
                    if (segEnd.before(begin)) {
                        segEnd = begin;
                    }
                    AppMachDaySegDTO seg = buildMachDaySegment(segBegin, segEnd, WorkState.IDLE);
                    segList.add(seg);
                    segBegin = begin.before(segEnd) ? segEnd : begin;
                    segEnd = end;
                    isRun = true;
                    isIdle = false;
                }
                // 工作->工作
                else if (isRun) {
                    segEnd = end;
                }
                // 初始化
                else {
                    isRun = true;
                }
            }
            // 结束
            if (i == machDataLogList.size() - 1) {
                WorkState workState =
                        isIdle ? WorkState.IDLE :
                                isRun ? WorkState.RUN : WorkState.STOP;
                AppMachDaySegDTO seg = buildMachDaySegment(segBegin, segEnd, workState);
                segList.add(seg);
            }
        }
        return segList;
    }


    /**
     * 获取网络离线分段
     *
     * @param machDataLogList
     * @return
     */
    public static List<AppMachDaySegDTO> separateNetworkSegment(List<? extends WorkStateDTO> machDataLogList) {
        // 数据分段
        List<AppMachDaySegDTO> segList = new ArrayList<>();

        Date begin = null;
        Integer netState = null;
        for (int i = 0; i < machDataLogList.size(); i++) {
            WorkStateDTO workStateDTO = machDataLogList.get(i);
            Integer netState1 = workStateDTO.getNetState() == null ? NetState.ONLINE.getValue() : workStateDTO.getNetState();
            Date time = workStateDTO.getTime();
            if (i == 0) {
                begin = time;
                netState = netState1;
                AppMachDaySegDTO appMachDaySegDTO = buildMachDaySegment(DateUtil.addMinutes(time, -1), time, null, netState1);
                segList.add(appMachDaySegDTO);
                continue;
            }
            // 在线状态相同
            if (netState1.equals(netState)) {
                begin = time;
                AppMachDaySegDTO appMachDaySegDTO = segList.get(segList.size() - 1);
                appMachDaySegDTO.setEndTime(time);
                appMachDaySegDTO.setTimes(MachUtil.calculateTime(appMachDaySegDTO.getStartTime(), time));
            }
            // 在线状态不相同
            if (!netState1.equals(netState)) {
                netState = netState1;
                AppMachDaySegDTO appMachDaySegDTO = buildMachDaySegment(begin, time, null, netState1);
                begin = time;
                segList.add(appMachDaySegDTO);
            }
        }

        return segList;
    }

    /**
     * 综合工作状态和网络状态分段
     *
     * @param workStateSegment
     * @param netStateSegment
     * @return
     */
    public static List<AppMachDaySegDTO> separateWorkStateAndNetState(List<AppMachDaySegDTO> workStateSegment, List<AppMachDaySegDTO> netStateSegment) {

        List<AppMachDaySegDTO> list = new ArrayList<>();
        for (AppMachDaySegDTO networkSegDTO : netStateSegment) {
            Integer netState = networkSegDTO.getNetState();
            Date networkStartTime = networkSegDTO.getStartTime();
            Date networkEndTime = networkSegDTO.getEndTime();

            // 将在线分段在细分工作分段
            if (NetState.ONLINE.getValue().equals(netState)) {
                for (AppMachDaySegDTO machDaySegDTO : workStateSegment) {
                    Date startTime = machDaySegDTO.getStartTime();
                    Date endTime = machDaySegDTO.getEndTime();

                    if (startTime.getTime() <= networkStartTime.getTime() && endTime.after(networkStartTime)) {
                        if (endTime.after(networkEndTime)) {
                            endTime = networkEndTime;
                        }
                        AppMachDaySegDTO data = new AppMachDaySegDTO();
                        data.setStartTime(networkStartTime);
                        data.setEndTime(endTime);
                        data.setTimes(MachUtil.calculateTime(networkStartTime, endTime));
                        data.setWorkState(machDaySegDTO.getWorkState());
                        list.add(data);
                        continue;
                    }
                    if (startTime.after(networkStartTime) && startTime.before(networkEndTime)) {
                        if (endTime.after(networkEndTime)) {
                            endTime = networkEndTime;
                        }
                        AppMachDaySegDTO data = new AppMachDaySegDTO();
                        data.setStartTime(startTime);
                        data.setEndTime(endTime);
                        data.setWorkState(machDaySegDTO.getWorkState());
                        data.setTimes(MachUtil.calculateTime(startTime, endTime));
                        list.add(data);
                        continue;
                    }
                }
            } else {
                list.add(networkSegDTO);
            }
        }
        Collections.sort(list, Comparator.comparing(o -> o.getStartTime()));
        return list;
    }

    /**
     * 合并相同工时分段
     *
     * @param segmentList
     * @return
     */
    public static List<AppMachDaySegDTO> mergeMachDaySegment(List<AppMachDaySegDTO> segmentList) {
        //相邻的"状态"分段合并
        if (segmentList.size() > 1) {
            for (int i = 1; i < segmentList.size(); i++) {
                AppMachDaySegDTO preSeg = segmentList.get(i - 1);
                AppMachDaySegDTO curSeg = segmentList.get(i);
                //合并相邻的同状态的分段
                if (MachUtil.isSameState(preSeg.getWorkState(), curSeg.getWorkState())) {
                    buildMachDaySegment(curSeg, preSeg.getStartTime(), null, null);
                    segmentList.remove(i - 1);
                    i--;
                }
            }
        }
        return segmentList;
    }

    /**
     * 合并相同的工时分段
     *
     * @param segmentList
     * @return
     */
    public static List<AppMachDaySegDTO> mergeSameStateSegment(List<AppMachDaySegDTO> segmentList) {
        // 相邻的"状态"分段合并
        if (segmentList.size() > 1) {
            for (int i = 1; i < segmentList.size(); i++) {
                AppMachDaySegDTO preSeg = segmentList.get(i - 1);
                AppMachDaySegDTO curSeg = segmentList.get(i);
                // 合并相邻的同状态的分段
                if (MachUtil.isSameState(preSeg.getWorkState(), curSeg.getWorkState()) ||
                        (MachUtil.isOfflineState(preSeg.getNetState()) && MachUtil.isOfflineState(curSeg.getNetState()))
                ) {
                    buildMachDaySegment(curSeg, preSeg.getStartTime(), null, null);
                    segmentList.remove(i - 1);
                    i--;
                }
            }
        }
        return segmentList;
    }

    /**
     * 修正数据分段(网络延迟造成的假离线问题)
     *
     * @param segmentList
     * @return
     */
    public static List<AppMachDaySegDTO> amendOfflineDataSegment(List<AppMachDaySegDTO> segmentList) {
        if (segmentList.size() > 1) {
            for (int i = 1; i < segmentList.size(); i++) {
                AppMachDaySegDTO preSeg = segmentList.get(i - 1);
                AppMachDaySegDTO curSeg = segmentList.get(i);
                Integer times = preSeg.getTimes();
                if (NetState.OFFLINE.getValue().equals(preSeg.getNetState()) && OFFLINE_INTERVAL_TIMES > times) {
                    buildMachDaySegment(curSeg, preSeg.getStartTime(), null, null);
                    segmentList.remove(i - 1);
                    i--;
                }
            }
        }

        for (int i = 0; i < segmentList.size(); i++) {
            AppMachDaySegDTO appMachDaySegDTO = segmentList.get(i);
            // 最后一个点
            if (i == segmentList.size() - 1) {
                if (NetState.OFFLINE.getValue().equals(appMachDaySegDTO.getNetState()) && OFFLINE_INTERVAL_TIMES > appMachDaySegDTO.getTimes()) {
                    segmentList.remove(i);
                    break;
                }
            }
        }
        return segmentList;
    }

    /**
     * 修改数据分段
     *
     * @param segmentList
     * @return
     */
    public static List<AppMachDaySegDTO> amendMachDaySegment(List<AppMachDaySegDTO> segmentList) {
        if (segmentList.size() >= 3) {
            for (int i = 1; i < segmentList.size() - 1; i++) {
                AppMachDaySegDTO curSeg = segmentList.get(i);
                AppMachDaySegDTO preSeg = segmentList.get(i - 1);
                AppMachDaySegDTO postSeg = segmentList.get(i + 1);
                boolean isAgain = false;
                //1-寻找5分钟以内的怠机/工作分段(curSeg)
                if (MachUtil.isPowerOn(curSeg.getWorkState()) && curSeg.getTimes() <= 5 * 60) {
                    int totalTimes = preSeg.getTimes() + postSeg.getTimes();
                    //2.1-满足条件一: 前后分段都是停机 and 停机时长>=8分钟 and 停机时长>3*(当前时长)
                    if (MachUtil.isPowerOff(preSeg.getWorkState()) && MachUtil.isPowerOff(postSeg.getWorkState())
                            && totalTimes >= 8 * 60 && totalTimes >= 3 * curSeg.getTimes()) {
                        //当前分段修正为停机
                        buildMachDaySegment(curSeg, preSeg.getStartTime(), postSeg.getEndTime(), WorkState.STOP);
                        //然后三个分段合并
                        segmentList.remove(i - 1);
                        i--;
                        segmentList.remove(i + 1);
                        isAgain = true;
                        logger.debug("[干扰修正-1],{}", JSONUtil.toString(curSeg));
                    }
                    //2.2-满足条件:搜索前后20分钟,寻找最近的超过8分钟的停机或最近的超过6分钟的开机,如果前后搜索到的都是停机,则当前分段修正为停机.
                    else {
                        //搜索前后20分钟,查找匹配的分段
                        MachSegmentWapper preMatchSegWapper = searchPreMachDaySegment(segmentList, i);
                        MachSegmentWapper postMatchSegWapper = searchPostMachDaySegment(segmentList, i);
                        boolean needAmend = false;
                        //前后都符合
                        if (preMatchSegWapper != null && MachUtil.isPowerOff(preMatchSegWapper.getSegment().getWorkState())
                                && postMatchSegWapper != null && MachUtil.isPowerOff(postMatchSegWapper.getSegment().getWorkState())) {
                            needAmend = true;
                        }
                        //前面符合,后面搜索结果为空
                        else if (preMatchSegWapper != null && MachUtil.isPowerOff(preMatchSegWapper.getSegment().getWorkState())
                                && postMatchSegWapper == null) {
                            needAmend = true;
                        }
                        //前面搜索结果为空,后面符合
                        else if (preMatchSegWapper == null
                                && postMatchSegWapper != null && MachUtil.isPowerOff(postMatchSegWapper.getSegment().getWorkState())) {
                            needAmend = true;
                        }
                        //分段需要被修改
                        if (needAmend) {
                            //当前分段修正为停机
                            buildMachDaySegment(curSeg, null, null, WorkState.STOP);
                            isAgain = true;
                            logger.debug("[干扰修正-2],{}", JSONUtil.toString(curSeg));
                        }
                    }
                }
                //3-合并相同的分段
                else if (MachUtil.isSameState(preSeg.getWorkState(), curSeg.getWorkState())) {
                    buildMachDaySegment(curSeg, preSeg.getStartTime(), null, null);
                    segmentList.remove(i - 1);
                    i--;
                    isAgain = true;
                    logger.debug("[状态合并-1],{}", JSONUtil.toString(curSeg));
                }
                //3-合并相同的分段
                else if (MachUtil.isSameState(curSeg.getWorkState(), postSeg.getWorkState())) {
                    buildMachDaySegment(curSeg, null, postSeg.getEndTime(), null);
                    segmentList.remove(i + 1);
                    isAgain = true;
                    logger.debug("[状态合并-2],{}", JSONUtil.toString(curSeg));
                }
                //如果分段进行了合并,则从头开始修正.
                if (isAgain) {
                    i = 1;
                }
            }
        }
        return segmentList;
    }

    /**
     * 查询前20分钟内的分段(最近的超过8分钟的停机 或者 超过6分钟的开机)
     *
     * @param segmentList
     * @param curIndex
     * @return
     */
    public static MachSegmentWapper searchPreMachDaySegment(List<AppMachDaySegDTO> segmentList, int curIndex) {
        AppMachDaySegDTO curSeg = segmentList.get(curIndex);
        Date minTime = DateUtil.addMinutes(curSeg.getStartTime(), -20);
        for (int i = curIndex - 1; i >= 0; i--) {
            AppMachDaySegDTO seg = segmentList.get(i);
            Date segStartTime = seg.getStartTime();
            Date segEndTime = seg.getEndTime();
            int segTimes = seg.getTimes();
            if (segEndTime.after(minTime)) {
                if (segStartTime.before(minTime)) {
                    segTimes = MachUtil.calculateTime(minTime, segEndTime);
                }
                if ((MachUtil.isPowerOff(seg.getWorkState()) && segTimes >= 8 * 60)
                        || (MachUtil.isPowerOn(seg.getWorkState()) && segTimes >= 6 * 60)) {
                    return new MachSegmentWapper(i, seg);
                }
            }
        }
        return null;
    }

    /**
     * 查询后20分钟内的分段(最近的超过8分钟的停机 或者 超过6分钟的开机)
     *
     * @param segmentList
     * @param curIndex
     * @return
     */
    public static MachSegmentWapper searchPostMachDaySegment(List<AppMachDaySegDTO> segmentList, int curIndex) {
        AppMachDaySegDTO curSeg = segmentList.get(curIndex);
        Date maxTime = DateUtil.addMinutes(curSeg.getEndTime(), 20);
        for (int i = curIndex + 1; i < segmentList.size(); i++) {
            AppMachDaySegDTO seg = segmentList.get(i);
            Date segStartTime = seg.getStartTime();
            Date segEndTime = seg.getEndTime();
            int segTimes = seg.getTimes();
            if (segStartTime.before(maxTime)) {
                if (segEndTime.after(maxTime)) {
                    segTimes = MachUtil.calculateTime(segStartTime, maxTime);
                }
                if ((MachUtil.isPowerOff(seg.getWorkState()) && segTimes >= 8 * 60)
                        || (MachUtil.isPowerOn(seg.getWorkState()) && segTimes >= 6 * 60)) {
                    return new MachSegmentWapper(i, seg);
                }
            }
        }
        return null;
    }

    /**
     * 构建工时分段
     *
     * @param startTime
     * @param endTime
     * @param workState
     * @return
     */
    public static AppMachDaySegDTO buildMachDaySegment(Date startTime, Date endTime, WorkState workState) {
        int times = MachUtil.calculateTime(startTime, endTime);
        AppMachDaySegDTO seg = new AppMachDaySegDTO();
        seg.setStartTime(startTime);
        seg.setEndTime(endTime);
        seg.setTimes(times);
        seg.setWorkState(workState.getValue());
        return seg;
    }

    /**
     * 更新工时分段
     *
     * @param seg
     * @param startTime
     * @param endTime
     * @param workState
     * @return
     */
    public static AppMachDaySegDTO buildMachDaySegment(AppMachDaySegDTO seg, Date startTime, Date endTime, WorkState workState) {
        if (startTime != null) {
            seg.setStartTime(startTime);
        }
        if (endTime != null) {
            seg.setEndTime(endTime);
        }
        if (workState != null) {
            seg.setWorkState(workState.getValue());
        }
        int times = MachUtil.calculateTime(seg.getStartTime(), seg.getEndTime());
        seg.setTimes(times);
        return seg;
    }

    /**
     * 增加工时分段
     *
     * @param startTime
     * @param endTime
     * @param workState
     * @param netState
     * @return
     */
    public static AppMachDaySegDTO buildMachDaySegment(Date startTime, Date endTime, Integer workState, Integer netState) {
        AppMachDaySegDTO seg = new AppMachDaySegDTO();
        seg.setStartTime(startTime);
        seg.setEndTime(endTime);
        seg.setWorkState(workState);
        seg.setNetState(netState);
        int times = MachUtil.calculateTime(seg.getStartTime(), seg.getEndTime());
        seg.setTimes(times);
        return seg;
    }


    public static void main(String[] args) {

        AppMachDaySegDTO data1 = new AppMachDaySegDTO();
        data1.setStartTime(DateUtil.parseDate("16:35:45", "HH:mm:ss"));
        data1.setEndTime(DateUtil.parseDate("18:12:31", "HH:mm:ss"));
        data1.setWorkState(1);

        AppMachDaySegDTO data2 = new AppMachDaySegDTO();
        data2.setStartTime(DateUtil.parseDate("18:12:31", "HH:mm:ss"));
        data2.setEndTime(DateUtil.parseDate("18:15:31", "HH:mm:ss"));
        data2.setWorkState(3);

        List<AppMachDaySegDTO> workStateList = new ArrayList();
        workStateList.add(data1);
        workStateList.add(data2);

        AppMachDaySegDTO data3 = new AppMachDaySegDTO();
        data3.setStartTime(DateUtil.parseDate("16:35:45", "HH:mm:ss"));
        data3.setEndTime(DateUtil.parseDate("16:49:16", "HH:mm:ss"));
        data3.setNetState(1);

        AppMachDaySegDTO data4 = new AppMachDaySegDTO();
        data4.setStartTime(DateUtil.parseDate("16:49:16", "HH:mm:ss"));
        data4.setEndTime(DateUtil.parseDate("16:49:50", "HH:mm:ss"));
        data4.setNetState(0);

        AppMachDaySegDTO data5 = new AppMachDaySegDTO();
        data5.setStartTime(DateUtil.parseDate("16:49:50", "HH:mm:ss"));
        data5.setEndTime(DateUtil.parseDate("18:15:31", "HH:mm:ss"));
        data5.setNetState(1);

        List<AppMachDaySegDTO> netStateList = new ArrayList();
        netStateList.add(data3);
        netStateList.add(data4);
        netStateList.add(data5);

        List<AppMachDaySegDTO> appMachDaySegDTOS = separateWorkStateAndNetState(workStateList, netStateList);
        System.out.println(appMachDaySegDTOS.toString());


    }


}
